package ink.xiaobaibai.rbac.voter;

import ink.xiaobaibai.common.MyRole;
import ink.xiaobaibai.rbac.FilterInvocationSecurityMetadataSourceImpl;
import javafx.util.Pair;
import org.springframework.security.access.AccessDecisionVoter;
import org.springframework.security.access.ConfigAttribute;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority;

import java.util.*;

/**
 * @description: 投票者
 * @author: 小白白
 * @create: 2021-05-15
 * 实现AccessDecisionVoter接口,自己构建一个投票者
 **/
public class RoleVoter implements AccessDecisionVoter<Object> {

    /**
     * 角色等级Map,角色名-等级 int越小,等级越高
     */
    private Map<String, Integer> roleLevelMap = new HashMap<>();

    /**
     * 传入角色等级
     *
     * @param roleLevelList
     */
    public RoleVoter(List<Pair<Integer, String>> roleLevelList) {

        for (int i = 0; i < roleLevelList.size(); i++) {
            //可能有相同等级
            this.roleLevelMap.put(roleLevelList.get(i).getValue(), roleLevelList.get(i).getKey());
        }
        this.roleLevelMap.put("ROLE_ANONYMOUS", Integer.MAX_VALUE);
    }

    //参考源码:RoleVoter类

    @Override
    public boolean supports(ConfigAttribute attribute) {
        return true;
    }

    @Override
    public boolean supports(Class<?> clazz) {
        return true;
    }

    /**
     * -1拒绝 0弃权 1赞同
     *
     * @param authentication
     * @param object
     * @param attributes
     * @return
     */
    @Override
    public int vote(Authentication authentication, Object object, Collection<ConfigAttribute> attributes) {
        if (authentication == null) {
            return -1;
        } else {

            //用户短时间内不会变,所以可以使用redis加快速度,但是用户角色不多,无需使用redis

            //当前角色可能有多个
            Collection<? extends GrantedAuthority> authorities = this.extractAuthorities(authentication);

            //当前角色迭代器
            Iterator<? extends GrantedAuthority> iterator = authorities.iterator();

            /**
             * 平级角色,不同资源,所以不能只获取当前角色的最高等级
             * 可能有两个最高等级
             */
            Set<String> set = new HashSet<>();
            //当前角色的最高等级
            boolean isUser = false;
            int maxLevel = Integer.MAX_VALUE;

            while (iterator.hasNext()) {
                //当前用户的角色
                String role = iterator.next().getAuthority();
                if (role.equals(MyRole.ADMIN)) {
                    //管理员必定通过
                    return 1;
                }

                Integer roleLevel = this.roleLevelMap.get(role);
                if (roleLevel < maxLevel) {
                    //非匿名角色
                    set.add(role);
                    isUser = true;
                }
                //低级,忽略
            }

            //当前资源需要的角色
            Iterator var6 = attributes.iterator();

            while (true) {
                ConfigAttribute attribute;
                do {
                    //遍历完了,居然还没有通过,就反对
                    if (!var6.hasNext()) {
                        return -1;
                    }

                    attribute = (ConfigAttribute) var6.next();

                    if (FilterInvocationSecurityMetadataSourceImpl.PUBLIC_SOURCE
                            .equals(attribute.getAttribute())) {
                        //公共资源,都可以访问
                        return 1;
                    }
                    if (FilterInvocationSecurityMetadataSourceImpl.PRIVATE_SOURCE
                            .equals(attribute.getAttribute())) {
                        //内部资源,有角色就可以访问
                        return isUser ? 1 : -1;
                    }

                    //当前资源需要的角色->attribute

                    String roleName = attribute.getAttribute();

                    if (set.contains(roleName)) {
                        return 1;
                    }

                } while (!this.supports(attribute));
            }
        }
    }

    private Collection<? extends GrantedAuthority> extractAuthorities(Authentication authentication) {
        return authentication.getAuthorities();
    }

}
